package com.yedean.factory.context;

import com.yedean.factory.BeansException;
import com.yedean.factory.ConfigurableListableBeanFactory;
import com.yedean.factory.config.BeanFactoryPostProcessor;
import com.yedean.factory.config.BeanPostProcessor;
import com.yedean.factory.io.DefaultResourceLoader;

import java.util.Map;

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        //1.刷新创建容器 并加载BeanDefiniton
        refreshBeanFactory();
        //2.获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //3.执行beanFactoryPostProcessor
        invokeBeanFactoryPostProcessor(beanFactory);
        //4.提前注册beanPostProcessor
        registerBeanPostProcessor(beanFactory);
        //5.提前实例化单例bean对象
        beanFactory.preInstantiateSingletons();

    }

    private void registerBeanPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = getBeansOfType(BeanPostProcessor.class);
        beanPostProcessorMap.forEach((x,y)->{
            beanFactory.addBeanPostProcessor(y);
        });
    }

    private void invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = getBeansOfType(BeanFactoryPostProcessor.class);
        beanFactoryPostProcessorMap.forEach((x,y)->{
            y.postProcessBeanFactory(beanFactory);
        });
    }


    /**
     * 创建容器并且加载定义
     * @throws BeansException
     */
    protected abstract void refreshBeanFactory()throws BeansException;


    /**
     * 获取bean工厂
     * @return ConfigurableListableBeanFactory
     */
    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> tClass) {
       return getBeanFactory().getBeansOfType(tClass);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) {
        return getBeanFactory().getBean(name,args);
    }

    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }
}
